﻿using GpuHud;
using System.Collections;
using System.Collections.Generic;
using GpuHud.Settings;
using TMPro;
using Unity.Collections;
using Unity.Mathematics;
using UnityEngine;
using UnityEngine.Profiling;

/// <summary>
/// Text数据中继器
/// </summary>
public partial class GpuHudDataAccessor 
{
    struct TextStyleAccessorOffset
    {
        public int accessorIdx;
        public int offset;
        public int atlasIdx;
        public bool hasRead;
    }
    //禁止其他分部类访问
    public NativeList<TextStyleShaderData> TextShaderCacheData;
    //Accessor列表
    private NativeList<int> antiGCTextAccessorList;
    //每个文字,对应的Accessor的下标,在对应Accessor中的偏移,以及使用的图集
    private NativeList<TextStyleAccessorOffset> contentOffset;
    //每个图集的使用次数(文字)
    private NativeHashMap<int, int> atlasUseCount;
    private NativeMultiHashMap<int, int> textAtlasPool;

    #region 标记参数
    string m_content;

    public string TextContent
    {
        get
        {
            return m_content;
        }
    }

    public int TextLength
    {
        get
        {
            return m_content.Length;
        }
    }

    Color m_curTextColor;
    public Color CurrentTextColor
    {
        get
        {
            return m_curTextColor;
        }
    }

    Vector3 m_currentTextPos;
    public Vector3 CurrentTextPosition
    {
        get
        {
            return m_currentTextPos;
        }
    }
    float m_currentScale;
    private bool isDispose;

    public float CurrentScale
    {
        get
        {
            return m_currentScale;
        }
    }

    private float originWidth;
    public float CurrentWidth{
        get
        {
            return originWidth * CurrentScale * fixedScale;
        }
    }

    private float fixedScale;

    #endregion

    /// <summary>
    /// 初始化TextAccessor
    /// </summary>
    /// <param name="asset"></param>
    internal void InitTextAccessor(GpuHudAsset asset)
    {
        m_curTextColor = asset.m_TextAssetList[0].ShaderOriginData.FaceColor;
        isDispose = false;
        if (asset.m_TextAssetList != null && asset.m_TextAssetList[0] != null)
        {
            antiGCTextAccessorList = new NativeList<int>(3,Allocator.Persistent);
            if (!contentOffset.IsCreated)
                contentOffset = new NativeList<TextStyleAccessorOffset>(2, Allocator.Persistent);
        }
    }

    /// <summary>
    /// 重构TextShader的数据
    /// 之前实现的方式是放到子Accessor里进行Rebuild,但是这样产生了一个问题,就是一段文字用多个图集时就必然会出错
    /// 所以这里考虑父DataAccessor接管TextAccessor的数据重构
    /// 
    /// 考虑一个问题,既然必须要接管,那么子Accessor是否还有必要存在
    /// 现在子Accessor的作用主要是:  
    /// 用来获取Collection的依赖,以及初始化时向Collection申请固定的数据空间,获取偏移
    /// </summary>
    public void RebuildTextShaderData()
    {
        if (!antiGCTextAccessorList.IsCreated) return;
        TextShaderCacheData = new NativeList<TextStyleShaderData>(m_content.Length + 1, Allocator.Temp);
        TextShaderCacheData.Length = m_content.Length + 1;
        m_currentScale = 1;

        ///现在是一个TextStyle/TextAsset一个Accessor
        ///需要改成 -> 一个Accessor里可以建立多个Texture,但是每个Texture对应一个Block
        GpuHudTextStyleAccessor textStyleAccessor;
        var FontAsset = gpuHudBaseEntity.GpuHudAsset.m_TextAssetList[0].FontAsset;
        var originAsset = gpuHudBaseEntity.GpuHudAsset.m_TextAssetList[0];
        float offset = 0;
        int i = 0;
        TextStyleShaderData tempStyle = default;
        var textures = FontAsset.atlasTextures;
        TMP_Character tmpCharacter;
        float maxSzY = -0;
        SetTextColor(gpuHudBaseEntity.GpuHudAsset.m_TextAssetList[0].ShaderOriginData.FaceColor, true);
        int minLen = TextLength;
        fixedScale = originAsset.OriginSize / 10;
        for (; i < minLen; i++)
        {
            tmpCharacter = null;
            //获取Text的UVRect(TMP里的)
            //Text Rebuild
            if (FontAsset.characterLookupTable.TryGetValue(TextContent[i], out tmpCharacter))
            {
                //TODO: 如果这里发生了多图集有做处理么? 没有
                // UVRect
                tmpCharacter = FontAsset.characterLookupTable[TextContent[i]];
                tempStyle.UVRect.x = (tmpCharacter.glyph.glyphRect.x - FontAsset.atlasPadding) / (float)FontAsset.atlasWidth;
                tempStyle.UVRect.y = (tmpCharacter.glyph.glyphRect.y - FontAsset.atlasPadding) / (float)FontAsset.atlasHeight;
                tempStyle.UVRect.z = (tmpCharacter.glyph.glyphRect.width + FontAsset.atlasPadding) / (float)FontAsset.atlasWidth;
                tempStyle.UVRect.w = (tmpCharacter.glyph.glyphRect.height + FontAsset.atlasPadding) / (float)FontAsset.atlasHeight;

                // Size scale相对缩放系数 转换到公制
                // todo Size统一化, 统一Asset中使用的Size与转换的Size
                // 设置基础Size为10,其他的Size均由10来缩放
                var scale = 10 / ((FontAsset.atlasHeight + tmpCharacter.glyph.metrics.height) * 0.5f);
                tempStyle.Size.y = tmpCharacter.glyph.metrics.height * scale;
                tempStyle.Size.x = tmpCharacter.glyph.metrics.width * scale;
                maxSzY = Mathf.Max(maxSzY, tempStyle.Size.y);
                // Position
                tempStyle.Position.xyz = (float3)Position + originAsset.Offset;

                var bearingX = tmpCharacter.glyph.metrics.horizontalBearingX * scale;
                //让Mesh以0为起点
                if (0 == i) offset = -bearingX;
                tempStyle.Position.w = offset + bearingX;
                tempStyle.LocalOffset.x = (tmpCharacter.glyph.metrics.horizontalBearingY - tmpCharacter.glyph.metrics.height) * scale;

                offset += tmpCharacter.glyph.metrics.horizontalAdvance * scale + originAsset.CharacterSpacing;
            }
            else
            {
                //todo 缺省字符
                tempStyle = default;
            }

            // FaceColor
            tempStyle.FaceColor     = originAsset.ShaderOriginData.FaceColor;
            tempStyle.OutLineColor  = originAsset.ShaderOriginData.OutLineColor;
            tempStyle.OutLineOffset = originAsset.ShaderOriginData.OutLineOffset;
            tempStyle.WeightBold    = originAsset.ShaderOriginData.WeightBold;
            tempStyle.Scale.x       = originAsset.OriginSize / 10;
            tempStyle.Scale.y       = 1;
            tempStyle.ItalicOffset  = originAsset.ItalicOffset;
            tempStyle.DepthOffset   = originAsset.ShaderOriginData.DepthOffset;
            
            tempStyle.OutLineWeightBold    = originAsset.ShaderOriginData.OutLineWeightBold;
            tempStyle.OutLineSoftness      = originAsset.ShaderOriginData.OutLineSoftness;
            tempStyle.FaceSoftness         = originAsset.ShaderOriginData.FaceSoftness;
            
            TextShaderCacheData[i] = tempStyle;
        }

        //居中
        //计算BoundaryBox的
        i = 0;
        originWidth = offset;
        offset /= 2;
        maxSzY /= 2;
        for (; i < minLen; i++)
        {
            tempStyle = TextShaderCacheData[i];
            tempStyle.Position.w -= offset;
            tempStyle.LocalOffset.y = maxSzY;
            //临时手动刷新, 全局刷新未开启
            //找到对应的accessor,设置ShaderData
            if (contentOffset[i].hasRead)
            {
                textStyleAccessor = GetAntiGCAccesoor(antiGCTextAccessorList[contentOffset[i].accessorIdx]);
                textStyleAccessor?.SetShaderData(tempStyle, contentOffset[i].offset);//Set Self Cache Data
            }
        }
        TextShaderCacheData.Dispose();
    }

    internal void SetTextOnScreen()
    {
        if (!antiGCTextAccessorList.IsCreated) return;

        foreach (var hookIdx in antiGCTextAccessorList)
        {
            var textAccessor = GetAntiGCAccesoor(hookIdx);
            if (textAccessor.isInUse)
            {
                textAccessor.SetOnScreen();
            }
        }
    }
    internal void SetTextOffScreen()
    {
        if (!antiGCTextAccessorList.IsCreated) return;

        foreach (var hookIdx in antiGCTextAccessorList)
        {
            var textAccessor = GetAntiGCAccesoor(hookIdx);
            if (textAccessor.isInUse)
            {
                textAccessor.SetOffScreen();
            }
        }
    }
    /// <summary>
    /// 申请的Native资源，在GC后要检查下，防止泄露
    /// </summary>
    ~GpuHudDataAccessor()
    {
        if (!isDispose)
        {
            DisposeTextAccessor();
        }
    }
    public void DisposeTextAccessor()
    {
        this.isDispose = true;
        if (contentOffset.IsCreated)
            contentOffset.Dispose();
        if (atlasUseCount.IsCreated)
            atlasUseCount.Dispose();
        if (TextShaderCacheData.IsCreated)
        {
            TextShaderCacheData.Dispose();
        }

        if (!antiGCTextAccessorList.IsCreated) return;
        //Debug.LogError("销毁: " + GetHashCode());
        foreach (var hookIdx in antiGCTextAccessorList)
        {
            var textAccessor = GetAntiGCAccesoor(hookIdx);
            textAccessor?.Dispose();
        }
        antiGCTextAccessorList.Dispose();
    }

    /// <summary>
    /// 对于文字而言,只有在对文本进行赋值的前提下才会进行Accessor的初始化
    /// 增量判断: 
    /// 1. 搜集
    /// 2. 丢弃与重建
    /// 3. 分配
    /// 上面流程是完整的更新文字流程,但因为现在文字不会频繁更新,所以不走这个流程
    /// </summary>
    /// <param name="content"></param>
    public void AlterTextStyleAccesor(string content,bool forceRebuild = true)
    {
        if (!antiGCTextAccessorList.IsCreated) return;
        if (this.m_content == content)
        {
            return;
        }
 
        this.m_content = content;
        var fontAsset = gpuHudBaseEntity.GpuHudAsset.m_TextAssetList[0].FontAsset;
        MaterialParamsSetting matParams =
            GpuHudFacade.GpuFacade.GetMatParamsByGroup(gpuHudBaseEntity.GpuHudAsset.m_TextAssetList[0].RenderGroupType);
        int blockCount = matParams.BatchCount;
        var textures = fontAsset.atlasTextures;
        //收集当前的Texture,同时添加文字到贴图上
        int atlasInUseDirty = 0;
        int maxIndex = 0;
        //先收集图集信息
        contentOffset.Clear();
        atlasUseCount = new NativeHashMap<int, int>(4, Allocator.Temp);
        textAtlasPool = new NativeMultiHashMap<int, int>(4, Allocator.Temp);

        if (gpuHudBaseEntity.isLocalizationDirty)
        {
            foreach (var hookIdx in antiGCTextAccessorList)
            {
                var textAccessor = GetAntiGCAccesoor(hookIdx);
                textAccessor.isInUse = false;
                textAccessor?.Dispose();
            }
            antiGCTextAccessorList.Clear();
            gpuHudBaseEntity.isLocalizationDirty = false;
        }

        //设置所有用到的的textStyle为false
        //這點複雜度會不會有點高
        for (int i = 0; i < content.Length; ++i)
        {
            if (fontAsset.characterLookupTable.ContainsKey(content[i]) || fontAsset.TryAddCharacters(new[] { (uint)content[i] }))
            {
                int atlasIndex = fontAsset.characterLookupTable[content[i]].glyph.atlasIndex;
                atlasInUseDirty |= (1 << atlasIndex);
                maxIndex = Mathf.Max(atlasIndex, maxIndex);
                if (atlasUseCount.ContainsKey(atlasIndex))
                {
                    atlasUseCount[atlasIndex] += 1;
                }
                else
                {
                    atlasUseCount.Add(atlasIndex, 1);
                }

                contentOffset.Add(new TextStyleAccessorOffset()
                {
                    accessorIdx = -1,
                    offset = 0,
                    atlasIdx = atlasIndex,
                    hasRead = false
                });
            }
            else
            {
                contentOffset.Add(new TextStyleAccessorOffset()
                {
                    accessorIdx = -1,
                    offset = 0,
                    atlasIdx = -1,
                    hasRead = false
                });
            }
        }
        NativeList<int> removeDirty = new NativeList<int>(3,Allocator.Temp);
        NativeList<int> keepHook = new NativeList<int>(3,Allocator.Temp);
        //丢弃与重建
        foreach(var hookIdx in antiGCTextAccessorList)
        {
            var accessor = GetAntiGCAccesoor(hookIdx);
            if(accessor==null) continue;
            //如果图集使用量大于0,复用当前accessor,重建
            if (atlasUseCount.ContainsKey(accessor.atlasIndex) && atlasUseCount[accessor.atlasIndex] > 0)
            {
                atlasUseCount[accessor.atlasIndex] -= blockCount;
                accessor.textCount = 0;
                accessor.isInUse = true;
                accessor.InitialShaderData();
                textAtlasPool.Add(accessor.atlasIndex, keepHook.Length);
                keepHook.Add(hookIdx);
            }
            else
            {
                //小于0,移除当前Accessor
                accessor.Dispose();
                accessor.isInUse = false;
                removeDirty.Add(hookIdx);
            }
        }
        //删除废弃Accessor
        for(int i=0;i< removeDirty.Length; ++i)
        {
            RemoveAntiGCAccessorHook(removeDirty[i]);
        }
        for(int i = 0; i < keepHook.Length; ++i)
        {
            antiGCTextAccessorList[i] = keepHook[i];
        }
        antiGCTextAccessorList.RemoveRange(keepHook.Length, antiGCTextAccessorList.Length);
        
        //遍历结束,再遍历一遍图集使用收集信息,如果有大于0的,则创建复数次acessor
        for (int i = 0; i <= maxIndex; ++i)
        {
            if (atlasUseCount.ContainsKey(i) && atlasUseCount[i] > 0)
            {
                int repeatTimes = Mathf.CeilToInt(1.0f * atlasUseCount[i] / blockCount);
                for (int j = 0; j < repeatTimes; ++j)
                {
                    var newTextStyle = new GpuHudTextStyleAccessor(gpuHudBaseEntity.GpuHudAsset.m_TextAssetList[0], this, textures[i]);
                    newTextStyle.atlasIndex = i;
                    newTextStyle.isInUse = true;
                    newTextStyle.textCount = 0;
                    int key = antiGCTextAccessorList.Length;
                    antiGCTextAccessorList.Add(HookAntiGCAccessor(newTextStyle));
                    textAtlasPool.Add(i, key);
                }
                atlasUseCount[i] = 0;
            }
        }
        for (int i = 0; i < content.Length; ++i)
        {
            int atlasIdx = contentOffset[i].atlasIdx;
            if (atlasIdx != -1)
            {
                //找第一个支持的Accessor
                NativeMultiHashMap<int, int>.Enumerator iter = textAtlasPool.GetValuesForKey(atlasIdx);

                foreach (var v in iter)
                {
                    GpuHudTextStyleAccessor accessor = GetAntiGCAccesoor(antiGCTextAccessorList[v]);
                    if (accessor.textCount < blockCount)
                    {
                        contentOffset[i] = new TextStyleAccessorOffset()
                        {
                            accessorIdx = v,
                            offset = accessor.textCount,
                            atlasIdx = atlasIdx,
                            hasRead = true
                        };
                        accessor.textCount++;
                        break;
                    }
                }
            }
        }

        if (forceRebuild)
        {
            RebuildTextShaderData();
        }
        removeDirty.Dispose();
        atlasUseCount.Dispose();
        textAtlasPool.Dispose();
        keepHook.Dispose();
    }

    public void SetTextPosition(Vector3 newPosition, bool force = false)
    {
        if (!force && newPosition == m_currentTextPos) return;
        m_currentTextPos = newPosition;
        foreach (var hookIdx in antiGCTextAccessorList)
        {
            var textAccessor = GetAntiGCAccesoor(hookIdx);
            textAccessor.SetNewPosition(newPosition);
        }
    }

    public void SetTextColor(Color newColor, bool force = false)
    {
        if (!force && newColor == m_curTextColor) return;
        m_curTextColor = newColor;
        foreach (var hookIdx in antiGCTextAccessorList)
        {
            var textAccessor = GetAntiGCAccesoor(hookIdx);
            textAccessor.SetNewColor(newColor);
        }
    }

    public void SetTextAlpha(float alpha, bool force = false)
    {
        if (!force && alpha == m_curTextColor.a) return;
        m_curTextColor.a = alpha;
        if(GpuHudFacade.GpuFacade.Context.CurrentRenderMethod == RenderMethod.GpuInstance)
        {
            foreach (var hookIdx in antiGCTextAccessorList)
            {
                var textAccessor = GetAntiGCAccesoor(hookIdx);
                textAccessor.SetNewAlpha(alpha);
            }
        }
        else
        {
            foreach (var hookIdx in antiGCTextAccessorList)
            {
                var textAccessor = GetAntiGCAccesoor(hookIdx);
                textAccessor.SetNewColor(m_curTextColor);
            }
        }
    }

    public void SetTextScale(float newScale, bool force = false)
    {
        if (!force && m_currentScale == newScale) return;
        m_currentScale = newScale;
        foreach (var hookIdx in antiGCTextAccessorList)
        {
            var textAccessor = GetAntiGCAccesoor(hookIdx);
            textAccessor.SetNewScale(newScale);
        }
    }
    
    
    #region antiGClist
    private GpuHudTextStyleAccessor GetAntiGCAccesoor(int idx)
    {
        GpuHudTextStyleAccessor accessor = GpuHudUtility.GetHookedObject<GpuHudTextStyleAccessor>(idx);
        return accessor;
    }

    private int HookAntiGCAccessor(GpuHudTextStyleAccessor accessor)
    {
        return GpuHudUtility.HookObject(accessor);
    }

    /// <summary>
    /// 将钩子接触(不删除列表)
    /// </summary>
    /// <returns></returns>
    private void RemoveAntiGCAccessorHook(int hookIdx)
    {
        GpuHudUtility.RemoveHookedObject(hookIdx);
    }

    #endregion
}
